﻿using Cloud.Update.Core.Models;
using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Cloud.Update.Core.Utils
{
    public class GeneralZipUtil
    {
        public delegate void ProgressChangedHandler(ZipModel progress);
        public event ProgressChangedHandler ProgressChanged;
        public delegate void UnZipFinishHandler(bool isSuccess, string msg = null);
        public event UnZipFinishHandler UnZipFinish;

        private string zipFilePath;
        private string unZipDir;


        public GeneralZipUtil(string zipFilePath, string unZipDir)
        {
            this.zipFilePath = zipFilePath;
            this.unZipDir = unZipDir;

        }
        /// <summary>  
        /// 功能：解压zip格式的文件。  
        /// </summary>  
        /// <param name="zipFilePath">压缩文件路径</param>  
        /// <param name="unZipDir">解压文件存放路径,为空时默认与压缩文件同一级目录下，跟压缩文件同名的文件夹</param>  
        /// <returns>解压是否成功</returns>  
        public void UnZip()
        {
            if (zipFilePath == string.Empty)
            {
                string msg = "压缩文件不能为空！";
                UnZipFinish?.Invoke(false, msg);
                throw new Exception(msg);
            }
            if (!File.Exists(zipFilePath))
            {
                string msg = "压缩文件不存在！";
                UnZipFinish?.Invoke(false, msg);
                throw new FileNotFoundException(msg);
            }
            //解压文件夹为空时默认与压缩文件同一级目录下，跟压缩文件同名的文件夹  
            if (unZipDir == string.Empty)
                unZipDir = zipFilePath.Replace(Path.GetFileName(zipFilePath), Path.GetFileNameWithoutExtension(zipFilePath));
            if (!unZipDir.EndsWith("/"))
                unZipDir += "/";
            try
            {
                if (!Directory.Exists(unZipDir))
                    Directory.CreateDirectory(unZipDir);
                FileInfo fileInfo = new FileInfo(zipFilePath);//获取文件
                using (var s = new ZipInputStream(File.OpenRead(zipFilePath)))
                {
                    //文件大小
                    var totalSize = fileInfo.Length / 1024;
                    long currentSize = 0;
                    ZipEntry theEntry;
                    while ((theEntry = s.GetNextEntry()) != null)
                    {
                        string directoryName = Path.GetDirectoryName(theEntry.Name);
                        string fileName = Path.GetFileName(theEntry.Name);
                        if (!string.IsNullOrEmpty(directoryName))
                        {
                            if (directoryName.Contains("\\")) {
                                var fileIndex = directoryName.IndexOf("\\");
                                directoryName = directoryName.Substring(fileIndex + 1);
                                Directory.CreateDirectory(unZipDir + directoryName);
                            }
                        }
                        
                        if (fileName != string.Empty)
                        {
                            var file = theEntry.Name;
                            if (file.Contains("\\")) {
                                file = file.Replace("\\", "/");
                            }
                            if (file.Contains("/"))
                            {
                                var fileIndex = file.IndexOf('/');
                                file = file.Substring(fileIndex + 1);
                            }                           
                            //fileName = unZipDir + theEntry.Name.Replace("/", "\\");
                            using (FileStream streamWriter = File.Create(unZipDir + file))
                            //using (FileStream streamWriter = File.Create(fileName))
                            {
                                int size;
                                byte[] data = new byte[2048];
                                while (true)
                                {
                                    size = s.Read(data, 0, data.Length);
                                    if (size > 0)
                                    {
                                        streamWriter.Write(data, 0, size);
                                        //界面显示相关
                                        currentSize += size;
                                        var sizeKb = currentSize / 1024;
                                        var rate = (float)sizeKb / (float)totalSize;
                                        if (rate > 1)
                                            rate = 1;
                                        ProgressChanged?.Invoke(new ZipModel { TotalSize = totalSize, CurrentSize = sizeKb, ProgressRate = (int)(rate * 100), Name = fileName });
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }                  
                }
                //解压完成 
                //将zip压缩包删除
                if (File.Exists(zipFilePath))
                {
                    File.Delete(zipFilePath);    //存在则删除
                }
                UnZipFinish?.Invoke(true, "解压完成!");
            }
            catch (Exception ex)
            {
                UnZipFinish?.Invoke(false, ex.Message);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="FileToZip">文件</param>
        /// <param name="ZipedFile">压缩到</param>
        public void Zip(string FileToZip, string ZipedFile)
        {//不加密压缩
            if (!File.Exists(FileToZip))
            {
                throw new FileNotFoundException("The specified file " + FileToZip + " could not be found. Zipping aborderd");
            }

            FileStream StreamToZip = new FileStream(FileToZip, FileMode.Open, FileAccess.Read);
            FileStream ZipFile = File.Create(ZipedFile);
            ZipOutputStream ZipStream = new ZipOutputStream(ZipFile);
            ZipEntry ZipEntry = new ZipEntry("ZippedFile");
            ZipStream.PutNextEntry(ZipEntry);
            ZipStream.SetLevel(4);//设置压缩级别。新关卡将立即激活。（1 到 9）。
            byte[] buffer = new byte[2048];
            int size = StreamToZip.Read(buffer, 0, buffer.Length);
            ZipStream.Write(buffer, 0, size);
            try
            {
                while (size < StreamToZip.Length)
                {
                    int sizeRead = StreamToZip.Read(buffer, 0, buffer.Length);
                    ZipStream.Write(buffer, 0, sizeRead);
                    size += sizeRead;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            ZipStream.Finish();
            ZipStream.Close();
            StreamToZip.Close();
        }

        public void ZipFileMain(string[] args, string password)
        {//加密压缩
            string[] filenames = Directory.GetFiles(args[0]);

            ZipOutputStream s = new ZipOutputStream(File.Create(args[1]));

            s.SetLevel(6); // 0 - store only to 9 - means best compression

            //s.Password = md5.encrypt(password);

            foreach (string file in filenames)
            {
                //打开压缩文件
                FileStream fs = File.OpenRead(file); 

                byte[] buffer = new byte[fs.Length];
                fs.Read(buffer, 0, buffer.Length);

                Array arr = file.Split('\\');
                string le = arr.GetValue(arr.Length - 1).ToString();
                ZipEntry entry = new ZipEntry(le);
                entry.DateTime = DateTime.Now;
                entry.Size = fs.Length;
                fs.Close();
                s.PutNextEntry(entry);
                s.Write(buffer, 0, buffer.Length);
            }
            s.Finish();
            s.Close();
        }

        #region "MD5加密"
        /// <summary>
        ///32位 MD5加密
        /// </summary>
        /// <param name="str">加密字符</param>
        /// <returns></returns>
        public string encrypt(string str)
        {
            string cl = str;
            string pwd = cl;
            //MD5 md5 = MD5.Create();
            //byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl));
            //for (int i = 0; i < s.Length; i++)
            //{
            //    pwd = pwd + s[i].ToString("X");
            //}
            return pwd;
        }
        #endregion
    }
}

